﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using GeneralDFS.BusinessLogical.IO;
using GeneralDFS.BusinessLogical.Nodes;
using GeneralDFS.Common.IO;
using GeneralDFS.DataEntity.IO;
using GeneralDFS.Interface;

namespace GeneralDFS.BusinessLogical.FileSystemLogical
{
     [Serializable]
    public class FileSystem : IFileSystem
    {
        private readonly FsImage _fsImage;
        private readonly DirectoryNodeInfo _rootInfo;
        private readonly Directory _rootDir;
        private readonly NameNode _nameNode;
        private readonly ReaderWriterLock _fsLock;
        private const int LockMaxTime = 10;

        /// <summary>
        /// FileSystem构造
        /// </summary>
        /// <param name="nameNode"></param>
        public FileSystem(NameNode nameNode)
        {
            _rootInfo = nameNode.Root;
            _nameNode = nameNode;
            _rootDir=new Directory(_rootInfo);
            if (_fsImage == null)
                _fsImage = new FsImage(_nameNode);
            _fsLock=new ReaderWriterLock();
        }
        
        #region 通过路径获取节点
        /// <summary>
        /// 通过路径获得目录节点
        /// </summary>
        /// <param name="path">完整的文件夹路径</param>
        /// <returns>返回目录节点</returns>
        public NodeBase GetNodeByPath(String path)
        {
            return GetNodeByPath(PathHelper.Separate(path));
        }

        /// <summary>
        /// 通过路径获得目录节点
        /// </summary>
        /// <param name="names"></param>
        /// <returns>返回目录节点</returns>
        public NodeBase GetNodeByPath(String[] names)
        {
            _fsLock.AcquireReaderLock(LockMaxTime);
            var nodes = _rootDir.GetNodesByPath(names, 1);
            if (nodes != null && nodes.Length > 0)
            {
                _fsLock.ReleaseReaderLock();
                return nodes[0];
            }
            _fsLock.ReleaseReaderLock();
            return null;
        }
        #endregion

        #region 通过路径删除节点
        /// <summary>
        /// 通过路径删除节点
        /// </summary>
        /// <param name="type">节点的类型“File”或者"Directory“</param>
        /// <param name="path">完整的路径</param>
        /// <returns>"File"或者"Directory"</returns>
        public Boolean DeleteNodeByPath(String path, string type)
        {
            return DeleteNodeByPath(PathHelper.Separate(path), type);
        }

        /// <summary>
        /// 通过路径删除节点
        /// </summary>
        /// <param name="pathName"></param>
        /// <param name="type">要删除的节点类型File或者Directory</param>
        /// <returns></returns>
        public Boolean DeleteNodeByPath(String[] pathName, string type)
        {
            try
            {
                _fsLock.AcquireWriterLock(LockMaxTime);
                var node = GetNodeByPath(pathName);
                var parentDirectory = new Directory(node.Parent);
                return parentDirectory.RemoveChildren(node.Name);
            }
            catch (Exception ex)
            {
                LogUtil.LogError(ex.Message);
            }
            finally
            {
                _fsLock.ReleaseWriterLock();
            }
            return false;
        }
        #endregion

        #region 根据路径判断是否存在该节点
        /// <summary>
        /// 判断是否存在该路径的文件
        /// </summary>
        /// <param name="path">完整路径</param>
        /// <returns>存在返回true，不存在返回false</returns>
        public bool ExistNode(string path)
        {
            _fsLock.AcquireReaderLock(LockMaxTime);
            var node = GetNodeByPath(path);
            _fsLock.ReleaseReaderLock();
            if (node != null)
                return true;
            return false;
        }
        #endregion

        #region 根据路径创建目录
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path">完整的文件路径</param>
        /// <returns></returns>
        public Boolean CreateDirectoryByPath(string path)
        {
            return CreateDirectoryByPath(PathHelper.Separate(path));
        }

        public Boolean CreateDirectoryByPath(string[] pathName)
        {
            if (pathName.Length > 1)
            {
                try
                {
                    _fsLock.AcquireWriterLock(LockMaxTime);
                    var parent = GetNodeByPath(PathHelper.GetParentPath(pathName));
                    if (parent == null)
                    {
                        CreateDirectoryByPath(PathHelper.GetParentPath(pathName)); //父节点目录不存在时，先创建父节点
                        parent = GetNodeByPath(PathHelper.GetParentPath(pathName));
                        LogUtil.LogDebug("create {0}",PathHelper.PathArraryToString(pathName));
                    }
                    var parentDirectory = new Directory(parent);
                    var children = new DirectoryNodeInfo {Name = pathName[pathName.Length - 1],CreateTime = DateTime.Now.ToFileTime()};
                    parentDirectory.AddChildren(children);
                    _fsLock.ReleaseWriterLock();
                    return true;
                }
                catch (Exception ex)
                {
                    LogUtil.LogError(string.Format("fail to createDirectory \n{0}", ex.Message));
                    _fsLock.ReleaseWriterLock();
                }
            }
            return false;
        }
        #endregion

        #region 根据路路径和文件节点创建文件节点
        /// <summary>
        /// 创建文件节点
        /// </summary>
        /// <param name="directoryPath">目录地址（不包含文件名）</param>
        /// <param name="node"></param>
        /// <returns></returns>
        public Boolean CreateFileByPath(string directoryPath, FileNodeInfo node)
        {
            return CreateFileByPath(PathHelper.Separate(directoryPath), node);
        }

        private bool CreateFileByPath(string[] pathName, FileNodeInfo node)
        {
            try
            {
                //_fsLock.AcquireReaderLock(LockMaxTime);
                //_fsLock.AcquireWriterLock(LockMaxTime);
                var parent = GetNodeByPath(pathName);
                if (parent == null)
                {
                    CreateDirectoryByPath(PathHelper.GetParentPath(pathName));
                    parent = GetNodeByPath(PathHelper.GetParentPath(pathName));
                }
                if (parent.IsDirectory())
                {
                    node.Parent = parent as DirectoryNodeInfo;
                    node.Depth = parent.Depth + 1;
                    var parentDirectory = new Directory(parent);
                    if (parentDirectory.AddChildren(node) >= 0)
                    {
                        //_fsLock.ReleaseReaderLock();
                        //_fsLock.ReleaseWriterLock();
                        return true;
                    }
                }
                //此处留着以后加上push error到client端
            }
            catch (Exception ex)
            {
                LogUtil.LogError(pathName.Length+PathHelper.PathArraryToString(pathName));
                LogUtil.LogError(string.Format("fail to createFile \n{0}", ex.Message));
                //_fsLock.ReleaseReaderLock();
                //_fsLock.ReleaseWriterLock();
            }
            return false;
        }

        public FileNodeInfo CreateFileByPath(String filePath)
        {
            var file = FileNodeInfo.CreateFile(filePath);
            CreateFileByPath(filePath, file);
            return file;
        }

        #endregion

        #region 获取根目录节点
        //获取根目录节点
        public DirectoryNodeInfo GetRootNode()
        {
            return _rootInfo;
        }
        #endregion

        #region FsImage处理
        /// <summary>
        /// 保存文件系统镜像
        /// </summary>
        public void SaveFsImage()
        {
            _fsLock.AcquireReaderLock(LockMaxTime);
            _fsLock.AcquireWriterLock(LockMaxTime);
            _fsImage.Save();
            _fsLock.ReleaseReaderLock();
            _fsLock.ReleaseWriterLock();
        }

        #endregion

        #region FileSystemLock

        public void WriteLock()
        {
            _fsLock.AcquireWriterLock(LockMaxTime);
        }

        public void WriteUnlock()
        {
            _fsLock.ReleaseWriterLock();
        }

        public void ReadLock()
        {
            _fsLock.AcquireReaderLock(LockMaxTime);
        }

        public void ReadUnlock()
        {
            _fsLock.ReleaseReaderLock();
        }
        #endregion

        public string ReplaceBlocks(string filePath, LocatedBlock[] locatedBlocks)
        {
            var node = GetNodeByPath(filePath);
            if (node != null && !node.isDirectory&&locatedBlocks!=null)
            {
                var fileNode = (FileNodeInfo) node;
                fileNode.Blocks=new FileBlockInfo[locatedBlocks.Length];
                for (var i = 0; i < fileNode.Blocks.Length; i++)
                {
                    fileNode.Blocks[i]=new FileBlockInfo(locatedBlocks[i].FileBlockMetaData,3);
                    foreach (var nodeID in locatedBlocks[i].DataNodeIds)
                    {
                        fileNode.Blocks[i].AddNode(_nameNode.DataNodeCenter.GetDataNodeByID(nodeID));
                        _nameNode.BlockManager.GetBlockMap().AddBlockInfo(fileNode.Blocks[i]);
                    }
                }
                fileNode.Blocks[0].IsSmallFile = locatedBlocks[0].FileBlockMetaData.IsSmallFile;
                return node.FullPath;
            }
            return null;
        }

        public List<LocatedBlock> GetLocatedBlocks(String filePath)
        {
            var node = GetNodeByPath(filePath);
            if (node != null && !node.isDirectory)
            {
                var fileNode = (FileNodeInfo)node;
                return fileNode.Blocks.Select(block => new LocatedBlock(block)).ToList();
            }
            return null;
        }
    }
}